/**
 * User: yeqingzhou01
 * Date: 13-10-10
 * Time: 下午5:52
 */
var errors = require('../../error/errors');
var dateUtils = require('../../utils/date-utils');

exports.dbsUtil = {
    mongodb: require('./db-mongodb-utils')
}
exports.daoOfTypes = {
    mongodb: function (collectionName, schema, schemaOptions) {
        var _this = this;
        var dbUtils = exports.dbsUtil.mongodb;
        _this.Schema = dbUtils.Schema;
        var options = schemaOptions ? schemaOptions : {
            autoIndex: true,
            safe: true,
            shardKey: { _id: 1 },
            collection: collectionName
        };
        var Dao = dbUtils.getDaoClass(collectionName, schema, options);

        _this.ObjectDaoClass = function (daoProperties) {
            var __this = this;
            var finalDao = dbUtils.getDaoWithClassAndObj(Dao, daoProperties);
            //save function
            /**
             * save current object
             * @param callback(numberAffected, [optional]errorMessage)
             * @returns {*}
             */
            __this.saveDao = function (callback) {
                finalDao.save(function (err, product, numberAffected) {
                    generateReturnMessage(
                        collectionName,
                        'saving',
                        err,
                        numberAffected,
                        callback
                    );
                });
            };
            /**
             * upsert from current obj
             * @param conditions
             * @param callback
             */
            __this.upsert = function (conditions, callback) {
                Dao.update(
                    conditions,
                    daoProperties,
                    {
                        safe: true,
                        upsert: true,
                        multi: true
                    },
                    function (err, numberAffected) {
                        generateReturnMessage(
                            collectionName,
                            'upserting',
                            err,
                            numberAffected,
                            callback
                        );
                    }
                );
            }
            __this.removeDao = function (conditions, callback) {
                Dao.remove(
                    conditions,
                    function (err, numberAffected) {
                        generateReturnMessage(
                            collectionName,
                            'removing',
                            err,
                            numberAffected,
                            callback
                        );
                    }
                )
            }
        }
        var dateTimeFields = (function () {
            var timeFields = [];
            for (var oneFieldKey in schema) {
                if (schema[oneFieldKey] == Date || schema[oneFieldKey].type == Date) {
                    timeFields.push(oneFieldKey);
                }
            }
            return timeFields;
        }());
        var changeResultDateTimeFieldsValue = function (result) {
            for (var oneFieldIdx in dateTimeFields) {
                if (result[dateTimeFields[oneFieldIdx]] != undefined) {
                    result[ dateTimeFields[oneFieldIdx] ] =
                        dateUtils.AniDate.getDateTimeStr(new Date(result[ dateTimeFields[oneFieldIdx] ]));
                }
            }
        }
        _this.generateDataFunctionsWithDao = function (DaoClassObj) {
            //find
            /**
             * Find with conditions
             * @param conditions
             * @param callback
             */
            DaoClassObj.findWithConditions = function (conditions, callback) {
                Dao.find(
                    conditions,
                    function (err, docs) {
                        generateFindMessage(collectionName, err, docs, callback);
                    }
                );
            }
            DaoClassObj.findPage = function (conditions, perPage, curPage, sortObj, callback) {
                Dao.find(
                    conditions,
                    {},
                    {
                        skip: perPage * curPage,
                        limit: perPage,
                        sort: sortObj
                    },
                    function (err, docs) {
                        generateFindMessage(collectionName, err, docs, callback);
                    }
                );
//                ).skip(perPage * curPage).limit(perPage);
            }
            DaoClassObj.batchSave = function (daoObjectsArr, callback) {
                return Dao.create(daoObjectsArr, function(err){
                    generateReturnMessage(
                        collectionName,
                        'BATCH SAVING...',
                        err,
                        'No answer...',
                        callback
                    );
                });
            }
            DaoClassObj.updateWithConditions = function (conditions, doc, isUpsert, callback) {
                return Dao.update(
                    conditions,
                    doc,
                    {
                        safe: true,
                        upsert: isUpsert,
                        multi: true
                    },
                    function (err, numberAffected) {
                        generateReturnMessage(
                            collectionName,
                            'UPDATING...',
                            err,
                            numberAffected,
                            callback
                        );
                    }
                );
            }
            //batch update functions
            /**
             * update or insert
             * @param conditions
             * @param doc
             * @param callback(numberAffected, [optional]errorMessage)
             * @returns [Query]
             */
            DaoClassObj.upsertWithConditions = function (conditions, doc, callback) {
                return Dao.update(
                    conditions,
                    doc,
                    {
                        safe: true,
                        upsert: true,
                        multi: true
                    },
                    function (err, numberAffected) {
                        generateReturnMessage(
                            collectionName,
                            'upserting',
                            err,
                            numberAffected,
                            callback
                        );
                    }
                );
            }
            DaoClassObj.removeWithConditions = function (conditions, callback) {
                Dao.remove(
                    conditions,
                    function (err, numberAffected) {
                        generateReturnMessage(
                            collectionName,
                            'removing',
                            err,
                            numberAffected,
                            callback
                        );
                    }
                )
            }

            DaoClassObj.aggregate = function(pipeline, opetions, callback){
                return Dao.aggregate(pipeline, options, callback);
            }

            /**
             * Generate map-reduce job
             * @param map
             * @param reduce
             * @param callback
             */
            DaoClassObj.mapReduce = function (query, map, reduce, options, callback) {
                options.query = query;
                options.map = map;
                options.reduce = reduce;

                Dao.mapReduce(
                    options,
                    function (err, daoModel, stats) {
                        if (err) {
                            callback(err.message);
                        } else {
                            callback(daoModel);
                        }
                    }
                );
            }
        }
    },
    sql: function (tableName, schema) {
        //TODO
    }
}
var generateReturnMessage = function (collectionName, operationName, err, numberAffected, callback) {
    if (err) {
        callback(
            numberAffected,
            generateErrorInstance(collectionName, operationName, err)
        );
    } else {
        callback(numberAffected);
    }
}
var generateFindMessage = function (collectionName, err, docs, callback) {
    if (err) {
        callback(
            generateErrorInstance(collectionName, 'FINDING', err)
        );
    } else {
        callback(
            docs
        );
    }
}
var generateErrorInstance = function (collectionName, operationName, err) {
    return new errors.getErrorInstance(
        errors.errorsName.data,
        'Data of collection ['
            + collectionName
            + '] '
            + operationName
            + ' failed: '
            + err + '\n'
    )
}
